package com.log4jviewer.filters;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Text;

import com.sun.org.apache.xml.internal.serialize.OutputFormat;
import com.sun.org.apache.xml.internal.serialize.XMLSerializer;

/**
 * Saving filter list to XML config file
 * 
 * @author <a href="mailto:rd.ryly@gmail.com">Ruslan Diachenko</a>
 */
public class ConfigFilterSaver {

    public void saveFilters(final String file, final List<FilterModel> filterList) throws IOException,
            ParserConfigurationException {
        // Contain XML file structure in a DOM hierarchy.
        Document dom = createDocument();
        // create the XML structure
        // create the root element <filter>
        Element rootElem = dom.createElement(FilterXmlConstants.FILTER);
        dom.appendChild(rootElem);

        for (int filterIndex = 0; filterIndex < filterList.size(); filterIndex++) {
            // For each Filter object create <rule> element and attach it to root
            Element filterElem = dom.createElement(FilterXmlConstants.RULE);
            rootElem.appendChild(filterElem);

            Element nameElem = dom.createElement(FilterXmlConstants.NAME);
            Text nameElemValue = dom.createTextNode(filterList.get(filterIndex).getFilterName());
            nameElem.appendChild(nameElemValue);
            filterElem.appendChild(nameElem);

            Element descrElem = dom.createElement(FilterXmlConstants.DESCRIPTION);
            Text descrElemValue = dom.createTextNode(filterList.get(filterIndex).getFilterDescr());
            descrElem.appendChild(descrElemValue);
            filterElem.appendChild(descrElem);

            for (int filterItemIndex = 0; filterItemIndex < filterList.get(filterIndex)
                    .getItemsCount(); filterItemIndex++) {
                FilterItemModel flitem = filterList.get(filterIndex).getItem(filterItemIndex);
                // For each FilterItem object create <item> element and attach it to rule
                Element itemElem = createFilterElement(dom, flitem);
                filterElem.appendChild(itemElem);
            }
        }
        // print to file
        OutputFormat format = new OutputFormat(dom);
        format.setIndenting(true);

        // to generate a file output use fileoutputstream instead of system.out
        FileOutputStream out = new FileOutputStream(new File(file));
        XMLSerializer serializer = new XMLSerializer(out, format);
        serializer.serialize(dom);
    }

    /**
     * Create a document object using which we create a xml tree in memory.
     * 
     * @throws ParserConfigurationException
     */
    private Document createDocument() throws ParserConfigurationException {
        // get an instance of factory
        DocumentBuilderFactory docBuildFactory = DocumentBuilderFactory.newInstance();
        // get an instance of builder
        DocumentBuilder docBuild = docBuildFactory.newDocumentBuilder();
        // create an instance of DOM
        return docBuild.newDocument();
    }

    /**
     * Create an XML Item element for each rule.
     * 
     * @param filterItem
     *            - The rule for which we need to create an XML Item representation.
     * @return XML Item element representing a rule.
     */
    private Element createFilterElement(final Document dom, final FilterItemModel filterItem) {
        Element itemElem = dom.createElement(FilterXmlConstants.RULE_ITEM);
        itemElem.appendChild(createItem(dom, filterItem, FilterXmlConstants.LOGIC_OPERAND));
        itemElem.appendChild(createItem(dom, filterItem, FilterXmlConstants.FIELD_TYPE));
        itemElem.appendChild(createItem(dom, filterItem, FilterXmlConstants.INCLUDE));
        itemElem.appendChild(createItem(dom, filterItem, FilterXmlConstants.MATCH_PATTERN));
        itemElem.appendChild(createItem(dom, filterItem, FilterXmlConstants.ENABLE));
        return itemElem;
    }

    private Element createItem(final Document dom, final FilterItemModel filterItem, final String elementType) {
        Text itemValue = null;

        if (FilterXmlConstants.LOGIC_OPERAND.equals(elementType)) {
            itemValue = dom.createTextNode(filterItem.getLogicOperand().getValue());
        } else if (FilterXmlConstants.FIELD_TYPE.equals(elementType)) {
            itemValue = dom.createTextNode(filterItem.getFieldType().getValue());
        } else if (FilterXmlConstants.INCLUDE.equals(elementType)) {
            itemValue = dom.createTextNode(Boolean.toString(filterItem.isInclude()));
        } else if (FilterXmlConstants.MATCH_PATTERN.equals(elementType)) {
            itemValue = dom.createTextNode(filterItem.getPattern());
        } else if (FilterXmlConstants.ENABLE.equals(elementType)) {
            itemValue = dom.createTextNode(Boolean.toString(filterItem.isEnabled()));
        }

        Element item = dom.createElement(elementType);
        item.appendChild(itemValue);
        return item;
    }
}